﻿namespace HZY.Repository.EntityFramework.Annotations;

/// <summary>
/// DbContext 配置注解
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class DbContextConfigAttribute : Attribute
{
    /// <summary>
    /// 命名规则默认 蛇形 命名 SysFunction => sys_function
    /// </summary>
    public NameRuleType NameRuleType { get; set; } = NameRuleType.SnakeCase;

    /// <summary>
    /// 实体命名空间
    /// </summary>
    public string EntityNamespace { get; set; }

    public DbContextConfigAttribute(string entityNamespace)
    {
        EntityNamespace = entityNamespace;
    }

    public DbContextConfigAttribute(string entityNamespace, NameRuleType nameRuleType)
    {
        EntityNamespace = entityNamespace;
        NameRuleType = nameRuleType;
    }

    /// <summary>
    /// 获取模型类型集合
    /// </summary>
    /// <param name="dbContextType"></param>
    /// <param name="predicate"></param>
    /// <returns></returns>
    public List<Type> GetModelTypes(Type dbContextType, Func<Type, bool>? predicate = null)
    {
        var assembly = dbContextType.Assembly;
        var types = (from w in assembly.ExportedTypes.ToList()
                     where w.IsClass && w.IsPublic && !w.IsGenericType
                     where w.GetInterface(nameof(IEntity)) != null
                     where !w.Name.StartsWith(nameof(DefaultEntity)) && !w.Name.Contains(nameof(FullEntity))
                     select w)
                    .WhereIf(predicate != null, predicate)
                    .Where(w => Regex.IsMatch(w.FullName!, EntityNamespace))
                    .ToList()
                    ;

        //扫描类型下面的 dbset model
        var propertyInfos = dbContextType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        var dbsets = propertyInfos.Where(w => w.PropertyType.Name == "DbSet`1");

        foreach (var dbset in dbsets)
        {
            if (dbset.PropertyType.GenericTypeArguments.Length <= 0) continue;

            var model = dbset.PropertyType.GenericTypeArguments[0];

            if (types.Any(w => w.FullName == model.FullName)) continue;

            types.Add(model);
        }

        return types;
    }

    /// <summary>
    /// 模型创建
    /// </summary>
    /// <param name="modelBuilder"></param>
    /// <param name="modelTypes"></param>
    /// <returns></returns>
    public List<Type> OnModelCreating(ModelBuilder modelBuilder, List<Type> modelTypes)
    {
        #region 自动注册 dbset

        var types = modelTypes;

        foreach (var type in types)
        {
            if (modelBuilder.Model.FindEntityType(type) != null)
            {
                continue;
            }

            modelBuilder.Model.AddEntityType(type);
        }

        #endregion

        #region 过滤软删除 条件是：实体必须继承自 IDeleteBaseEntity

        var deleteBaseEntitys = modelBuilder.Model
            .GetEntityTypes()
            .Where(w => typeof(IDeleteEntity).IsAssignableFrom(w.ClrType))
            ;
        foreach (var item in deleteBaseEntitys)
        {
            var lambda = ExpressionTreeExtensions.Equal(item.ClrType, nameof(IDeleteEntity.IsDeleted), false);
            modelBuilder.Entity(item.ClrType).HasQueryFilter(lambda);
        }

        #endregion

        #region 自动映射表名

        // 默认是实体的名称 如: DbSet<Flow> Flows 表名就是 Flows 否则会根据 实体名称
        // 生成名称与所想不一致请查看实体名称  实体名称请使用驼峰命名法
        switch (NameRuleType)
        {
            case NameRuleType.Default:
                break;
            case NameRuleType.SnakeCase:
                // 蛇形命名法
                // ToUnderlineNomenclature()  将驼峰命名法改为蛇形命名法  类似: SysFunction => sys_function
                TableNameMapping(modelBuilder, oldTableName => oldTableName.ToUnderlineNomenclature());
                break;
            case NameRuleType.UpperSnakeCase:
                // 全大写蛇形命名法
                //  SysFunction => SYS_FUNCTION
                TableNameMapping(modelBuilder, oldTableName => oldTableName.ToUnderlineNomenclature().ToUpper());
                break;
            case NameRuleType.Upper:
                // 表名全大写
                TableNameMapping(modelBuilder, oldTableName => oldTableName.ToUpper());
                break;
            case NameRuleType.Lower:
                // 表名全小写
                TableNameMapping(modelBuilder, oldTableName => oldTableName.ToLower());
                break;
        }

        #endregion

        return types;
    }

    /// <summary>
    /// 数据库表名映射
    /// </summary>
    /// <param name="modelBuilder"></param>
    /// <param name="mappingName"></param>
    protected void TableNameMapping(ModelBuilder modelBuilder, Func<string, string> mappingName)
    {
        foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            string name = string.Empty;
            var tableAttr = entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).FirstOrDefault() as TableAttribute;
            if (tableAttr != null)
            {
                name = mappingName(tableAttr.Name);
                entity.SetTableName(name);
                continue;
            }
            name = mappingName(entity.GetTableName()!);
            entity.SetTableName(name);
        }
    }


}
